package utilities;

import java.util.ArrayList;
import viz_main.TreeComponent;

/*
 * Techniques borrowed from 
 * Jonathan Hodgson (2004) 
 * originally written in C#
 * http://www.codeproject.com/Articles/7039/Squarified-Treemaps-in-XAML-C-using-Microsoft-Long
 * 
 * Transposed and modified by Ken Calder and Lanz Singbeil
 */
public class TreeMapBuilder {
	
	private float tmp_dHeight, tmp_dWidth;
	private float height, width;
	private ArrayList<TreeComponent> treeComponents;
	private float offsetX = 0;
	private float offsetY = 0;
	
	public TreeMapBuilder(float width, float height, float offX, float offY, ArrayList<TreeComponent> t){
		tmp_dHeight = height;
		tmp_dWidth = width;
		this.width = width;
		this.height = height;
		offsetX = offX;
		offsetY = offY;
		treeComponents = t;
	}

	public void CalcMap()
	{  
	   //System.out.println("starting calcMap");
	   int start = 0;
	   int end = 0;

	   boolean vert;

	   float aspectCurr = 99999;
	   float aspectLast;
	   
	   vert = drawVert(tmp_dWidth, tmp_dHeight);
	   
	   // note: this may be the same as getting the recursive total...
	   // compute the total amount of data we are dealing with
	   int dataTotal = 0;
	   for (int n=0; n < treeComponents.size(); n++)
	   {
	      dataTotal += treeComponents.get(n).getNode().getCount();
	   }
	   float valueScale = ((width * height) / dataTotal) / 100;

	   // Iterate through AGAIN to scale the size by a percentage value so that it fits better
	   for (int n=0; n < treeComponents.size(); n++)
	   {
		   treeComponents.get(n).setTmpSize(valueScale * treeComponents.get(n).getNode().getCount());
	   }
	   
	   while (end != treeComponents.size()){

		   while (end != treeComponents.size())
		   {
		      aspectLast = Try(start, end, vert);
		      // System.out.print(aspectLast + ", ");
	
		      // Is aspect ratio worse than previous ratio?
		      if ((aspectLast > aspectCurr) || (aspectLast < 1))
		      {
		         float currX = 0;
		         float currY = 0;
	
		         // Lock the previous items in place
		         for (int n = start; n < end; n++)
		         {
		            treeComponents.get(n).setXLoc(offsetX + currX);
		            treeComponents.get(n).setYLoc(offsetY + currY);
		            
		            if (vert)
		               currY += treeComponents.get(n).getYSize();
		            else
		               currX += treeComponents.get(n).getXSize();
		         }
	
		         if (vert)
		            offsetX += treeComponents.get(start).getXSize();
		         else
		            offsetY += treeComponents.get(start).getYSize();
		         
		         tmp_dWidth = width - offsetX;
		         tmp_dHeight = height - offsetY;
	
		         vert = drawVert(tmp_dWidth, tmp_dHeight);
		         //System.out.println("computed : " + start);
		         start = end;
		         end = start;
		         aspectCurr = 99999;
		         
		         break;
		      }
		      else
		      {
		         // Store newly calculate sizes
		         for (int n = start; n <= end; n++)
		         {
		        	 treeComponents.get(n).setXSize(treeComponents.get(n).getTmpWidth());
		        	 treeComponents.get(n).setYSize(treeComponents.get(n).getTmpHeight());
		         }
		         aspectCurr = aspectLast;
		      }
	
		      // try to draw another item
		      end++;
		   }
	   }

	   // Set each item in the positions in the remaining area
	   float currX1 = 0;
	   float currY1 = 0;

	   for (int n = start; n < end; n++)
	   {
		   treeComponents.get(n).setXLoc(offsetX + currX1);
		   treeComponents.get(n).setYLoc(offsetY + currY1);

	      if (vert)
	         currY1 += treeComponents.get(n).getYSize();
	      else
	         currX1 += treeComponents.get(n).getXSize();
	   }
	   //System.out.println("ending calcMap");
	}
	
	private float Try(int start, int end, boolean vert)
	{
	   float total = 0; 
	   float aspect = 0;
	   float localWidth;
	   float localHeight;
	   
	   for (int n = start; n <= end; n++)
	   {
	      total += treeComponents.get(n).getTmpSize();
	   }

	   // Scale as needed for the width or height
	   if (vert)
	   {
	      localWidth = (total / tmp_dHeight) * 100;
	      localHeight = tmp_dHeight;
	   }
	   else
	   {
	      localHeight = (total / tmp_dWidth) * 100;
	      localWidth = tmp_dWidth;
	   }

	   for (int n=start; n <= end; n++)
	   {
	      if (vert)
	      {
	    	  treeComponents.get(n).setTmpWidth(localWidth);
	    	  treeComponents.get(n).setTmpHeight((float)(localHeight * 
	                 (treeComponents.get(n).getTmpSize() / total)));
	      }
	      else
	      {
	    	  treeComponents.get(n).setTmpWidth((float)(localWidth * 
		                 (treeComponents.get(n).getTmpSize() / total)));
	    	  treeComponents.get(n).setTmpHeight(localHeight);
	      }

	      aspect = Math.max(treeComponents.get(n).getTmpHeight() / 
	    		  treeComponents.get(n).getTmpWidth(),
	    		  treeComponents.get(n).getTmpWidth() / 
	    		  treeComponents.get(n).getTmpHeight());
	   }

	   return aspect;
	}
	
	private boolean drawVert(float width, float height)
	{
	   return width > height;
	}
}
